﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.IO;
using Google.Protobuf;
using Game.Protocol;

namespace MMOArpgServer.Net
{
    public class ClientToken
    {

        private Socket _Sokcet;
        private SocketAsyncEventArgs _ReadAsyncEventArgs;
        private SocketAsyncEventArgs _WriteAsyncEventArgs;
        private List<byte> _ReceiveCache;
        private int _AccountID=-1;
        private int _CharacterID=-1;
        Queue<byte[]> writeQueue = new Queue<byte[]>();
        private ServerBase _ServerBase;
        private bool _IsSendCompeleted=true;
       
        public Socket Sokcet
        {
            get
            {
                return _Sokcet;
            }

            set
            {
                _Sokcet = value;
            }
        }

        public int AccountID
        {
            get
            {
                return _AccountID;
            }

            set
            {
                _AccountID = value;
            }
        }

        public ServerBase ServerBase
        {
            get
            {
                return _ServerBase;
            }

            set
            {
                _ServerBase = value;
            }
        }

        public int CharacterID
        {
            get
            {
                return _CharacterID;
            }

            set
            {
                _CharacterID = value;
            }
        }



        public ClientToken()
        {
            _ReadAsyncEventArgs = new SocketAsyncEventArgs();
            _WriteAsyncEventArgs = new SocketAsyncEventArgs();

            _ReadAsyncEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
            _ReadAsyncEventArgs.UserToken = this;
            _ReadAsyncEventArgs.SetBuffer(new byte[1024], 0, 1024);

            _WriteAsyncEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
            _WriteAsyncEventArgs.UserToken = this;

            _ReceiveCache = new List<byte>();

        }
     
        private void IO_Completed(object sender, SocketAsyncEventArgs eventArgs)
        {
            switch (eventArgs.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    ProcessReceive(_ReadAsyncEventArgs);
                    break;
                case SocketAsyncOperation.Send:                  
                    ProcessSend(_WriteAsyncEventArgs);
                    break;
            }
        }
        
        public void StartReceive()
        {
            if (!this.Sokcet.Connected)
                return;
            bool isWillRaiseEvent = this.Sokcet.ReceiveAsync(_ReadAsyncEventArgs);
            if (!isWillRaiseEvent)
            {
                ProcessReceive(_ReadAsyncEventArgs);
            }
        }

        private void ProcessReceive(SocketAsyncEventArgs receiveEventArgs)
        {
            if (receiveEventArgs.BytesTransferred > 0 && receiveEventArgs.SocketError == SocketError.Success)
            {
                byte[] cacheBuffer = new byte[receiveEventArgs.BytesTransferred];
                Buffer.BlockCopy(receiveEventArgs.Buffer,0, cacheBuffer,0, cacheBuffer.Length);
                _ReceiveCache.AddRange(cacheBuffer);
                byte[] decodeBuffer = GetDecodeBuffer();
                PackageHead head = PackageHead.Parser.ParseFrom(decodeBuffer);
                if (head != null)
                {
                    DispatchManager.GetInstance().ServerMsgDispatcher.Dispatch(head.Type,head.Area,head.Command,decodeBuffer);
                }
            }
            StartReceive();
        }

        protected byte[] GetDecodeBuffer() {

            if (_ReceiveCache.Count < 4)
            {
                return  null;
            }
            MemoryStream ms = new MemoryStream(_ReceiveCache.ToArray());
            BinaryReader br = new BinaryReader(ms);
            int length = br.ReadInt32();
            if (length > ms.Length - ms.Position)
            {
                return  null;
            }
            byte[] buffer = new byte[length];
            buffer = br.ReadBytes(buffer.Length);
            _ReceiveCache.Clear();
            _ReceiveCache.AddRange(br.ReadBytes((int)(ms.Length - ms.Position)));
            ms.Close();
            br.Close();
            return buffer;
        }

        public void SendMsg(byte[] sendBuffer) {
            writeQueue.Enqueue(sendBuffer);
            if (_IsSendCompeleted) {
                _IsSendCompeleted = false;
                StartSend();
            }
        }

        public void StartSend() {
            if (writeQueue.Count == 0)
            {
                _IsSendCompeleted = true;
                return;
            }
            byte[] sendBuffer = writeQueue.Dequeue();
            try
            {             
                _WriteAsyncEventArgs.SetBuffer(sendBuffer, 0, sendBuffer.Length);
                bool isWillRaiseEvent = this.Sokcet.SendAsync(_WriteAsyncEventArgs);             
                if (!isWillRaiseEvent)
                {
                    ProcessSend(_WriteAsyncEventArgs);
                }
             
            }
            catch (SocketException  e) {
                Console.WriteLine("异常{0}  _WriteAsyncEventArgs当前状态 {1}", e.Message, _WriteAsyncEventArgs.SocketError);
            }
        }

        private void ProcessSend(SocketAsyncEventArgs writeAsyncEventArgs)
        {
            StartSend();
        }

       
        public void DisConnect() {
            _ReceiveCache.Clear();
            _Sokcet.Close();

            if (CharacterID != -1)
            {
                Pool.ClientPool.GetInstance().RemoveClient(CharacterID);
                CharacterID = -1;
                AccountID = -1;
            }
            else {
                Pool.ClientPool.GetInstance().RemoveNoID(this);
            }
        }
    }
}
